#include <osg/Geometry>
#include <osg/Group>
#include <osg/Texture2D>
#include <osg/TextureCubeMap>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>

osg::ref_ptr<osg::Geometry> createPlane( float length, float width )
{
	osg::ref_ptr<osg::Vec3Array> vec = new osg::Vec3Array;
	vec->resize( 8 );
	(*vec)[0] = osg::Vec3f(  length, 0, width )*0.5;
	(*vec)[1] = osg::Vec3f( -length, 0, width )*0.5;
	(*vec)[2] = osg::Vec3f( -length, 0,-width )*0.5;
	(*vec)[3] = osg::Vec3f(  length, 0,-width )*0.5;

	osg::ref_ptr<osg::Vec3Array> nor = new osg::Vec3Array;
	nor->push_back( osg::Vec3f( 0.0, -1.0, 0.0 ) );

	osg::ref_ptr<osg::Vec2Array> tex = new osg::Vec2Array;
	tex->push_back( osg::Vec2f(5.0, 5.0) );
	tex->push_back( osg::Vec2f(0.0, 5.0) );
	tex->push_back( osg::Vec2f(0.0, 0.0) );
	tex->push_back( osg::Vec2f(5.0, 0.0) );

	osg::ref_ptr<osg::Geometry> geo = new osg::Geometry;
	geo->setVertexArray( vec.get() );
	geo->setTexCoordArray( 0, tex.get() );
	geo->setNormalArray( nor.get() );
	geo->setNormalBinding( osg::Geometry::BIND_PER_PRIMITIVE_SET );
	geo->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, 4 ) );

	return geo;
}

osg::TextureCubeMap* readCubeMap()
{
	osg::TextureCubeMap* cubemap = new osg::TextureCubeMap;

	osg::Image* imagePosX = osgDB::readImageFile( "cubemap/posx.bmp" );
	osg::Image* imageNegX = osgDB::readImageFile( "cubemap/negx.bmp" );
	osg::Image* imagePosY = osgDB::readImageFile( "cubemap/posy.bmp" );
	osg::Image* imageNegY = osgDB::readImageFile( "cubemap/negy.bmp" );
	osg::Image* imagePosZ = osgDB::readImageFile( "cubemap/posz.bmp" );
	osg::Image* imageNegZ = osgDB::readImageFile( "cubemap/negz.bmp" );

	if (imagePosX && imageNegX && imagePosY /*&& imageNegY && imagePosZ && imageNegZ*/)
	{
		cubemap->setImage(osg::TextureCubeMap::POSITIVE_X, imagePosX);
		cubemap->setImage(osg::TextureCubeMap::NEGATIVE_X, imageNegX);
		cubemap->setImage(osg::TextureCubeMap::POSITIVE_Y, imagePosY);
		cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Y, imageNegY);
		cubemap->setImage(osg::TextureCubeMap::POSITIVE_Z, imagePosZ);
		cubemap->setImage(osg::TextureCubeMap::NEGATIVE_Z, imageNegZ);

		cubemap->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
		cubemap->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
		cubemap->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);

		cubemap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
		cubemap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	}

	return cubemap;
}


class updateShader : public osg::Uniform::Callback
{
public:
	updateShader() : time(0.0), dir(true) {}
	virtual void operator() ( osg::Uniform* uniform, osg::NodeVisitor* nv )
	{
		time += 0.03;
		uniform->set( time );
	}

	bool dir;
	float time;
};

int main(int argc, char** argv)
{
	osg::Group* root = new osg::Group;
	osg::Geode* model = new osg::Geode;
	model->addDrawable( createPlane(5, 5).get() );
	//osg::Node* model = osgDB::readNodeFile( "cow.osg" );
	root->addChild( model );

	osg::StateSet* stateset = model->getOrCreateStateSet();
	osg::Shader* vertexShader = new osg::Shader( osg::Shader::VERTEX );
	osg::Shader* fragmentShader = new osg::Shader( osg::Shader::FRAGMENT );
	vertexShader->loadShaderSourceFromFile( "ocean.vert" );
	fragmentShader->loadShaderSourceFromFile( "ocean.frag" );

	osg::Program* program = new osg::Program;
	program->addShader( vertexShader );
	program->addShader( fragmentShader );
	stateset->setAttributeAndModes( program, osg::StateAttribute::ON );

	osg::Uniform* param1 = new osg::Uniform( "LightPos", osg::Vec3(1.0, 1.0, 0.0) );
	stateset->addUniform( param1 );
	osg::Uniform* param2 = new osg::Uniform( "time", 0.0f );
	param2->setUpdateCallback( new updateShader );
	stateset->addUniform( param2 );
	osg::Uniform* param3 = new osg::Uniform( "normalMap", 0 );
	stateset->addUniform( param3 );
	osg::Uniform* param4 = new osg::Uniform( "cubeMap", 1 );
	stateset->addUniform( param4 );

	osg::Image* image = osgDB::readImageFile( "./water.bmp" );
	osg::Texture2D* tex = new osg::Texture2D( image );
	tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
	tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
	tex->setWrap(osg::Texture::WRAP_R, osg::Texture::REPEAT);
	tex->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
	tex->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	stateset->setTextureAttributeAndModes( 0, tex, osg::StateAttribute::ON );

	stateset->setTextureAttributeAndModes( 1, readCubeMap(), osg::StateAttribute::ON );

	osgViewer::Viewer viewer;
	viewer.setSceneData( root );
	viewer.realize();
	
	return viewer.run();
}
